﻿using Efcore.MultiTenant.CacheKey;
using Efcore.MultiTenant.Entity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using MySqlConnector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Efcore.MultiTenant.DbProvider
{
    internal class DbProvider<TContext> : IDbProvider<TContext> where TContext:BaseDbContext
    {
        private readonly IConnectionResolve _connectionResolve;
        private readonly IEntityScanner _entityScanner;
        private readonly TenantOption _tenantOption;

        public DbProvider(IConnectionResolve<TContext> connectionResolve, IEntityScanner entityScanner, TenantOption<TContext> tenantOption)
        {
            _connectionResolve = connectionResolve;
            _entityScanner = entityScanner;
            _tenantOption = tenantOption;
        }
        public void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //数据库连接
            switch (_connectionResolve.DbType)
            {
                case DbType.Mysql:
                    optionsBuilder.UseMySql(_connectionResolve.ConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
                    break;
                case DbType.SqlServer:
                    optionsBuilder.UseSqlServer(_connectionResolve.ConnectionString);
                    break;
                case DbType.Oracle:
                    optionsBuilder.UseOracle(_connectionResolve.ConnectionString, option => option.UseOracleSQLCompatibility("12"));
                    break;
                case DbType.Npgsql:
                    optionsBuilder.UseNpgsql(_connectionResolve.ConnectionString);
                    break;
                case DbType.Sqlite:
                    optionsBuilder.UseSqlite(_connectionResolve.ConnectionString);
                    break;
                default:
                    throw new Exception("no database type is matched");
            }

            //多租户类型为分表时
            if (_connectionResolve.TenantType == TenantType.Table)
            {
                _ = optionsBuilder.ReplaceService<IModelCacheKeyFactory, TenantModelCacheKeyFactory<TContext>>();
            }
            
        }

        public void OnModelCreating(ModelBuilder modelBuilder)
        {
            ////执行自定义ModelBuilder方法
            //_tenantOption.DbModelBuildAction?.Invoke(modelBuilder);

            //假如没有自动注入实体模型，增加efcore entity
            var existEntityList = modelBuilder.Model.GetEntityTypes();
            _entityScanner.ScanTypeList().ForEach(m =>
            {
                var existEntity = existEntityList.FirstOrDefault(j => j.ShortName() == m.Name);
                if (existEntity == null)
                {
                    modelBuilder.Model.AddEntityType(m);
                }
            });

            //实体映射表名
            existEntityList = modelBuilder.Model.GetEntityTypes();
            foreach (var item in existEntityList)
            {
                modelBuilder.Entity(item.ClrType).ToTable(GetTableName(item));
            }
        }

        /// <summary>
        /// 获取实体表名
        /// </summary>
        private string GetTableName(IMutableEntityType mutableEntityType)
        {
            var tableName = mutableEntityType.ShortName();
            switch (_tenantOption.TableNameType)
            {
                case TableNameType.ToLowercase:
                    tableName = tableName.ToLowerInvariant();
                    break;
                case TableNameType.ToUppercase:
                    tableName = tableName.ToUpperInvariant();
                    break;
                case TableNameType.ToUnderlineSegmentation:
                    var underLineName = new StringBuilder();
                    underLineName.Append(tableName[0]);
                    for (var i = 1; i < tableName.Length; i++)
                    {
                        underLineName.Append(tableName[i] >= 'A' && tableName[i] <= 'Z' ? $"_{tableName[i]}".ToLowerInvariant() : tableName[i]);
                    }
                    tableName = underLineName.ToString();
                    break;
            }

            //若是分表多租户
            if (_tenantOption.TenantType == TenantType.Table && !string.IsNullOrWhiteSpace(_connectionResolve.TenantInfo.TenantId))
            {
                tableName = $"{_connectionResolve.TenantInfo.TenantId}_{tableName}";
            }

            return tableName;
        }
    }
}
